using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml;
using ICSharpCode.TextEditor.Formatting.TSqlFormatter.Interfaces;

namespace ICSharpCode.TextEditor.Formatting.TSqlFormatter.Formatters
{
    public class TSqlStandardFormatter : ISqlTreeFormatter
    {
        private class TSqlStandardFormattingState : BaseFormatterState
        {
            private static Regex _startsWithBreakChecker = new Regex("^\\s*(\\r|\\n)", RegexOptions.None);

            private static Regex _lineBreakMatcher = new Regex("(\\r|\\n)+", RegexOptions.Compiled);

            private Dictionary<int, string> _mostRecentKeywordsAtEachLevel = new Dictionary<int, string>();

            private string IndentString { get; set; }

            private int IndentLength { get; set; }

            private int MaxLineWidth { get; set; }

            public bool StatementBreakExpected { get; set; }

            public bool BreakExpected { get; set; }

            public bool WordSeparatorExpected { get; set; }

            public bool SourceBreakPending { get; set; }

            public int AdditionalBreaksExpected { get; set; }

            public bool UnIndentInitialBreak { get; set; }

            public int IndentLevel { get; private set; }

            public int CurrentLineLength { get; private set; }

            public bool CurrentLineHasContent { get; private set; }

            public SpecialRegionType? SpecialRegionActive { get; set; }

            public XmlNode RegionStartNode { get; set; }

            public bool StartsWithBreak => _startsWithBreakChecker.IsMatch(_outBuilder.ToString());

            public bool OutputContainsLineBreak => _lineBreakMatcher.IsMatch(_outBuilder.ToString());

            public TSqlStandardFormattingState(bool htmlOutput, string indentString, int spacesPerTab, int maxLineWidth, int initialIndentLevel)
                : base(htmlOutput)
            {
                IndentLevel = initialIndentLevel;
                base.HtmlOutput = htmlOutput;
                IndentString = indentString;
                MaxLineWidth = maxLineWidth;
                int num = indentString.Split('\t').Length - 1;
                int num2 = num * (spacesPerTab - 1);
                IndentLength = indentString.Length + num2;
            }

            public TSqlStandardFormattingState(TSqlStandardFormattingState sourceState)
                : base(sourceState.HtmlOutput)
            {
                IndentLevel = sourceState.IndentLevel;
                base.HtmlOutput = sourceState.HtmlOutput;
                IndentString = sourceState.IndentString;
                IndentLength = sourceState.IndentLength;
                MaxLineWidth = sourceState.MaxLineWidth;
                CurrentLineLength = IndentLevel * IndentLength;
                CurrentLineHasContent = sourceState.CurrentLineHasContent;
            }

            public override void AddOutputContent(string content)
            {
                if (!SpecialRegionActive.HasValue)
                {
                    AddOutputContent(content, null);
                }
            }

            public override void AddOutputContent(string content, string htmlClassName)
            {
                if (CurrentLineHasContent && content.Length + CurrentLineLength > MaxLineWidth)
                {
                    WhiteSpace_BreakToNextLine();
                }
                if (!SpecialRegionActive.HasValue)
                {
                    base.AddOutputContent(content, htmlClassName);
                }
                CurrentLineHasContent = true;
                CurrentLineLength += content.Length;
            }

            public override void AddOutputLineBreak()
            {
                if (!SpecialRegionActive.HasValue)
                {
                    base.AddOutputLineBreak();
                }
                CurrentLineLength = 0;
                CurrentLineHasContent = false;
            }

            internal void AddOutputSpace()
            {
                if (!SpecialRegionActive.HasValue)
                {
                    _outBuilder.Append(" ");
                }
            }

            public void Indent(int indentLevel)
            {
                for (int i = 0; i < indentLevel; i++)
                {
                    if (!SpecialRegionActive.HasValue)
                    {
                        _outBuilder.Append(IndentString);
                    }
                    CurrentLineLength += IndentLength;
                }
            }

            internal void WhiteSpace_BreakToNextLine()
            {
                AddOutputLineBreak();
                Indent(IndentLevel);
                BreakExpected = false;
                SourceBreakPending = false;
                WordSeparatorExpected = false;
            }

            public void Assimilate(TSqlStandardFormattingState partialState)
            {
                CurrentLineLength += partialState.CurrentLineLength;
                CurrentLineHasContent = CurrentLineHasContent || partialState.CurrentLineHasContent;
                if (!SpecialRegionActive.HasValue)
                {
                    _outBuilder.Append(partialState.DumpOutput());
                }
            }

            public TSqlStandardFormattingState IncrementIndent()
            {
                IndentLevel++;
                return this;
            }

            public TSqlStandardFormattingState DecrementIndent()
            {
                IndentLevel--;
                return this;
            }

            public void SetRecentKeyword(string ElementName)
            {
                if (!_mostRecentKeywordsAtEachLevel.ContainsKey(IndentLevel))
                {
                    _mostRecentKeywordsAtEachLevel.Add(IndentLevel, ElementName.ToUpperInvariant());
                }
            }

            public string GetRecentKeyword()
            {
                string result = null;
                int? num = null;
                foreach (int key in _mostRecentKeywordsAtEachLevel.Keys)
                {
                    if ((!num.HasValue || num.Value > key) && key >= IndentLevel)
                    {
                        num = key;
                        result = _mostRecentKeywordsAtEachLevel[key];
                    }
                }
                return result;
            }

            public void ResetKeywords()
            {
                List<int> list = new List<int>();
                foreach (int key in _mostRecentKeywordsAtEachLevel.Keys)
                {
                    if (key >= IndentLevel)
                    {
                        list.Add(key);
                    }
                }
                foreach (int item in list)
                {
                    _mostRecentKeywordsAtEachLevel.Remove(item);
                }
            }
        }

        public enum SpecialRegionType
        {
            NoFormat = 1,
            Minify
        }

        public IDictionary<string, string> KeywordMapping = new Dictionary<string, string>();

        public TSqlStandardFormatterOptions Options { get; private set; }

        [Obsolete("Use Options.IndentString instead")]
        public string IndentString
        {
            get
            {
                return Options.IndentString;
            }
            set
            {
                Options.IndentString = value;
            }
        }

        [Obsolete("Use Options.SpacesPerTab instead")]
        public int SpacesPerTab
        {
            get
            {
                return Options.SpacesPerTab;
            }
            set
            {
                Options.SpacesPerTab = value;
            }
        }

        [Obsolete("Use Options.MaxLineWidth instead")]
        public int MaxLineWidth
        {
            get
            {
                return Options.MaxLineWidth;
            }
            set
            {
                Options.MaxLineWidth = value;
            }
        }

        [Obsolete("Use Options.ExpandCommaLists instead")]
        public bool ExpandCommaLists
        {
            get
            {
                return Options.ExpandCommaLists;
            }
            set
            {
                Options.ExpandCommaLists = value;
            }
        }

        [Obsolete("Use Options.TrailingCommas instead")]
        public bool TrailingCommas
        {
            get
            {
                return Options.TrailingCommas;
            }
            set
            {
                Options.TrailingCommas = value;
            }
        }

        [Obsolete("Use Options.SpaceAfterExpandedComma instead")]
        public bool SpaceAfterExpandedComma
        {
            get
            {
                return Options.SpaceAfterExpandedComma;
            }
            set
            {
                Options.SpaceAfterExpandedComma = value;
            }
        }

        [Obsolete("Use Options.ExpandBooleanExpressions instead")]
        public bool ExpandBooleanExpressions
        {
            get
            {
                return Options.ExpandBooleanExpressions;
            }
            set
            {
                Options.ExpandBooleanExpressions = value;
            }
        }

        [Obsolete("Use Options.ExpandBetweenConditions instead")]
        public bool ExpandCaseStatements
        {
            get
            {
                return Options.ExpandCaseStatements;
            }
            set
            {
                Options.ExpandCaseStatements = value;
            }
        }

        [Obsolete("Use Options.ExpandCaseStatements instead")]
        public bool ExpandBetweenConditions
        {
            get
            {
                return Options.ExpandBetweenConditions;
            }
            set
            {
                Options.ExpandBetweenConditions = value;
            }
        }

        [Obsolete("Use Options.UppercaseKeywords instead")]
        public bool UppercaseKeywords
        {
            get
            {
                return Options.UppercaseKeywords;
            }
            set
            {
                Options.UppercaseKeywords = value;
            }
        }

        [Obsolete("Use Options.BreakJoinOnSections instead")]
        public bool BreakJoinOnSections
        {
            get
            {
                return Options.BreakJoinOnSections;
            }
            set
            {
                Options.BreakJoinOnSections = value;
            }
        }

        [Obsolete("Use Options.HTMLColoring instead")]
        public bool HTMLColoring
        {
            get
            {
                return Options.HTMLColoring;
            }
            set
            {
                Options.HTMLColoring = value;
            }
        }

        public bool HTMLFormatted => Options.HTMLColoring;

        public string ErrorOutputPrefix { get; set; }

        public TSqlStandardFormatter()
            : this(new TSqlStandardFormatterOptions())
        {
        }

        public TSqlStandardFormatter(TSqlStandardFormatterOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }
            Options = options;
            if (options.KeywordStandardization)
            {
                KeywordMapping = StandardKeywordRemapping.Instance;
            }
            ErrorOutputPrefix = "--WARNING! ERRORS ENCOUNTERED DURING SQL PARSING!" + Environment.NewLine;
        }

        [Obsolete("Use the constructor with the TSqlStandardFormatterOptions parameter")]
        public TSqlStandardFormatter(string indentString, int spacesPerTab, int maxLineWidth, bool expandCommaLists, bool trailingCommas, bool spaceAfterExpandedComma, bool expandBooleanExpressions, bool expandCaseStatements, bool expandBetweenConditions, bool breakJoinOnSections, bool uppercaseKeywords, bool htmlColoring, bool keywordStandardization)
        {
            Options = new TSqlStandardFormatterOptions
            {
                IndentString = indentString,
                SpacesPerTab = spacesPerTab,
                MaxLineWidth = maxLineWidth,
                ExpandCommaLists = expandCommaLists,
                TrailingCommas = trailingCommas,
                SpaceAfterExpandedComma = spaceAfterExpandedComma,
                ExpandBooleanExpressions = expandBooleanExpressions,
                ExpandBetweenConditions = expandBetweenConditions,
                ExpandCaseStatements = expandCaseStatements,
                UppercaseKeywords = uppercaseKeywords,
                BreakJoinOnSections = breakJoinOnSections,
                HTMLColoring = htmlColoring,
                KeywordStandardization = keywordStandardization
            };
            if (keywordStandardization)
            {
                KeywordMapping = StandardKeywordRemapping.Instance;
            }
            ErrorOutputPrefix = "--WARNING! ERRORS ENCOUNTERED DURING SQL PARSING!" + Environment.NewLine;
        }

        public string FormatSQLTree(XmlDocument sqlTreeDoc)
        {
            TSqlStandardFormattingState tSqlStandardFormattingState = new TSqlStandardFormattingState(Options.HTMLColoring, Options.IndentString, Options.SpacesPerTab, Options.MaxLineWidth, 0);
            if (sqlTreeDoc.SelectSingleNode(string.Format("/{0}/@{1}[.=1]", "SqlRoot", "errorFound")) != null)
            {
                tSqlStandardFormattingState.AddOutputContent(ErrorOutputPrefix);
            }
            XmlNodeList rootList = sqlTreeDoc.SelectNodes(string.Format("/{0}/*", "SqlRoot"));
            ProcessSqlNodeList(rootList, tSqlStandardFormattingState);
            WhiteSpace_BreakAsExpected(tSqlStandardFormattingState);
            if (tSqlStandardFormattingState.SpecialRegionActive == SpecialRegionType.NoFormat)
            {
                XmlNode sqlTreeFragment = ExtractXmlBetween(tSqlStandardFormattingState.RegionStartNode, sqlTreeDoc.DocumentElement);
                TSqlIdentityFormatter tSqlIdentityFormatter = new TSqlIdentityFormatter(Options.HTMLColoring);
                tSqlStandardFormattingState.AddOutputContentRaw(tSqlIdentityFormatter.FormatSQLTree(sqlTreeFragment));
            }
            else if (tSqlStandardFormattingState.SpecialRegionActive == SpecialRegionType.Minify)
            {
                XmlNode fragmentNode = ExtractXmlBetween(tSqlStandardFormattingState.RegionStartNode, sqlTreeDoc.DocumentElement);
                TSqlObfuscatingFormatter tSqlObfuscatingFormatter = new TSqlObfuscatingFormatter();
                if (HTMLFormatted)
                {
                    tSqlStandardFormattingState.AddOutputContentRaw(Utils.HtmlEncode(tSqlObfuscatingFormatter.FormatSQLTree(fragmentNode)));
                }
                else
                {
                    tSqlStandardFormattingState.AddOutputContentRaw(tSqlObfuscatingFormatter.FormatSQLTree(fragmentNode));
                }
            }
            return tSqlStandardFormattingState.DumpOutput();
        }

        private void ProcessSqlNodeList(XmlNodeList rootList, TSqlStandardFormattingState state)
        {
            foreach (XmlElement root in rootList)
            {
                ProcessSqlNode(root, state);
            }
        }

        private void ProcessSqlNode(XmlElement contentElement, TSqlStandardFormattingState state)
        {
            int indentLevel = state.IndentLevel;
            if (contentElement.GetAttribute("hasError") == "1")
            {
                state.OpenClass("SQLErrorHighlight");
            }
            switch (contentElement.Name)
            {
                case "SqlStatement":
                    WhiteSpace_SeparateStatements(contentElement, state);
                    state.ResetKeywords();
                    ProcessSqlNodeList(contentElement.SelectNodes("*"), state);
                    state.StatementBreakExpected = true;
                    break;
                case "Clause":
                    state.UnIndentInitialBreak = true;
                    ProcessSqlNodeList(contentElement.SelectNodes("*"), state.IncrementIndent());
                    state.DecrementIndent();
                    if (Options.NewClauseLineBreaks > 0)
                    {
                        state.BreakExpected = true;
                    }
                    if (Options.NewClauseLineBreaks > 1)
                    {
                        state.AdditionalBreaksExpected = Options.NewClauseLineBreaks - 1;
                    }
                    break;
                case "SetOperatorClause":
                    state.DecrementIndent();
                    state.WhiteSpace_BreakToNextLine();
                    state.WhiteSpace_BreakToNextLine();
                    ProcessSqlNodeList(contentElement.SelectNodes("*"), state.IncrementIndent());
                    state.BreakExpected = true;
                    state.AdditionalBreaksExpected = 1;
                    break;
                case "BatchSeparator":
                    state.WhiteSpace_BreakToNextLine();
                    ProcessSqlNodeList(contentElement.SelectNodes("*"), state);
                    state.BreakExpected = true;
                    break;
                case "DDLProceduralBlock":
                case "DDLOtherBlock":
                case "DDLDeclareBlock":
                case "CursorDeclaration":
                case "BeginTransaction":
                case "SaveTransaction":
                case "CommitTransaction":
                case "RollbackTransaction":
                case "ContainerOpen":
                case "ContainerClose":
                case "WhileLoop":
                case "IfStatement":
                case "SelectionTarget":
                case "ContainerContentBody":
                case "CTEWithClause":
                case "PermissionsBlock":
                case "PermissionsDetail":
                case "MergeClause":
                case "MergeTarget":
                    ProcessSqlNodeList(contentElement.SelectNodes("*"), state);
                    break;
                case "Input":
                case "BooleanExpression":
                case "LowerBound":
                case "UpperBound":
                    WhiteSpace_SeparateWords(state);
                    ProcessSqlNodeList(contentElement.SelectNodes("*"), state);
                    break;
                case "ContainerSingleStatementBody":
                case "ContainerMultiStatementBody":
                case "MergeAction":
                    {
                        bool flag = contentElement.SelectSingleNode("SqlStatement/Clause/IfStatement") != null;
                        if (flag && contentElement.ParentNode.Name.Equals("ElseClause"))
                        {
                            state.DecrementIndent();
                        }
                        else
                        {
                            state.BreakExpected = true;
                        }
                        ProcessSqlNodeList(contentElement.SelectNodes("*"), state);
                        if (flag && contentElement.ParentNode.Name.Equals("ElseClause"))
                        {
                            state.IncrementIndent();
                        }
                        state.StatementBreakExpected = false;
                        state.UnIndentInitialBreak = false;
                        break;
                    }
                case "PermissionsTarget":
                case "PermissionsRecipient":
                case "DDLWith":
                case "MergeCondition":
                case "MergeThen":
                    state.BreakExpected = true;
                    state.UnIndentInitialBreak = true;
                    ProcessSqlNodeList(contentElement.SelectNodes("*"), state.IncrementIndent());
                    state.DecrementIndent();
                    break;
                case "JoinOn":
                    if (Options.BreakJoinOnSections)
                    {
                        state.BreakExpected = true;
                    }
                    ProcessSqlNodeList(contentElement.SelectNodes("ContainerOpen"), state);
                    if (Options.BreakJoinOnSections)
                    {
                        state.IncrementIndent();
                    }
                    ProcessSqlNodeList(contentElement.SelectNodes("ContainerContentBody"), state);
                    if (Options.BreakJoinOnSections)
                    {
                        state.DecrementIndent();
                    }
                    break;
                case "CTEAlias":
                    state.UnIndentInitialBreak = true;
                    ProcessSqlNodeList(contentElement.SelectNodes("*"), state);
                    break;
                case "ElseClause":
                    ProcessSqlNodeList(contentElement.SelectNodes("ContainerOpen"), state.DecrementIndent());
                    ProcessSqlNodeList(contentElement.SelectNodes("ContainerSingleStatementBody"), state.IncrementIndent());
                    break;
                case "DDLAsBlock":
                case "CursorForBlock":
                    state.BreakExpected = true;
                    ProcessSqlNodeList(contentElement.SelectNodes("ContainerOpen"), state.DecrementIndent());
                    state.BreakExpected = true;
                    ProcessSqlNodeList(contentElement.SelectNodes("ContainerContentBody"), state);
                    state.IncrementIndent();
                    break;
                case "TriggerCondition":
                    state.DecrementIndent();
                    state.WhiteSpace_BreakToNextLine();
                    ProcessSqlNodeList(contentElement.SelectNodes("*"), state.IncrementIndent());
                    break;
                case "CursorForOptions":
                case "CTEAsBlock":
                    state.BreakExpected = true;
                    ProcessSqlNodeList(contentElement.SelectNodes("ContainerOpen"), state.DecrementIndent());
                    ProcessSqlNodeList(contentElement.SelectNodes("ContainerContentBody"), state.IncrementIndent());
                    break;
                case "DDLReturns":
                case "MergeUsing":
                case "MergeWhen":
                    state.BreakExpected = true;
                    state.UnIndentInitialBreak = true;
                    ProcessSqlNodeList(contentElement.SelectNodes("*"), state);
                    break;
                case "Between":
                    ProcessSqlNodeList(contentElement.SelectNodes("ContainerOpen"), state);
                    state.IncrementIndent();
                    ProcessSqlNodeList(contentElement.SelectNodes("LowerBound"), state.IncrementIndent());
                    if (Options.ExpandBetweenConditions)
                    {
                        state.BreakExpected = true;
                    }
                    ProcessSqlNodeList(contentElement.SelectNodes("ContainerClose"), state.DecrementIndent());
                    ProcessSqlNodeList(contentElement.SelectNodes("UpperBound"), state.IncrementIndent());
                    state.DecrementIndent();
                    state.DecrementIndent();
                    break;
                case "DDLDetailParens":
                case "FunctionParens":
                    state.WordSeparatorExpected = false;
                    WhiteSpace_BreakAsExpected(state);
                    state.AddOutputContent(FormatOperator("("), "SQLOperator");
                    ProcessSqlNodeList(contentElement.SelectNodes("*"), state.IncrementIndent());
                    state.DecrementIndent();
                    WhiteSpace_BreakAsExpected(state);
                    state.AddOutputContent(FormatOperator(")"), "SQLOperator");
                    state.WordSeparatorExpected = true;
                    break;
                case "DDLParens":
                case "ExpressionParens":
                case "SelectionTargetParens":
                case "InParens":
                    {
                        WhiteSpace_SeparateWords(state);
                        if (contentElement.Name.Equals("ExpressionParens") || contentElement.Name.Equals("InParens"))
                        {
                            state.IncrementIndent();
                        }
                        state.AddOutputContent(FormatOperator("("), "SQLOperator");
                        TSqlStandardFormattingState tSqlStandardFormattingState = new TSqlStandardFormattingState(state);
                        ProcessSqlNodeList(contentElement.SelectNodes("*"), tSqlStandardFormattingState);
                        if (tSqlStandardFormattingState.BreakExpected || tSqlStandardFormattingState.OutputContainsLineBreak)
                        {
                            if (!tSqlStandardFormattingState.StartsWithBreak)
                            {
                                state.WhiteSpace_BreakToNextLine();
                            }
                            state.Assimilate(tSqlStandardFormattingState);
                            state.WhiteSpace_BreakToNextLine();
                        }
                        else
                        {
                            state.Assimilate(tSqlStandardFormattingState);
                        }
                        state.AddOutputContent(FormatOperator(")"), "SQLOperator");
                        if (contentElement.Name.Equals("ExpressionParens") || contentElement.Name.Equals("InParens"))
                        {
                            state.DecrementIndent();
                        }
                        state.WordSeparatorExpected = true;
                        break;
                    }
                case "BeginEndBlock":
                case "TryBlock":
                case "CatchBlock":
                    if (contentElement.ParentNode.Name.Equals("Clause") && contentElement.ParentNode.ParentNode.Name.Equals("SqlStatement") && contentElement.ParentNode.ParentNode.ParentNode.Name.Equals("ContainerSingleStatementBody"))
                    {
                        state.DecrementIndent();
                    }
                    ProcessSqlNodeList(contentElement.SelectNodes("ContainerOpen"), state);
                    ProcessSqlNodeList(contentElement.SelectNodes("ContainerMultiStatementBody"), state);
                    state.DecrementIndent();
                    state.BreakExpected = true;
                    ProcessSqlNodeList(contentElement.SelectNodes("ContainerClose"), state);
                    state.IncrementIndent();
                    if (contentElement.ParentNode.Name.Equals("Clause") && contentElement.ParentNode.ParentNode.Name.Equals("SqlStatement") && contentElement.ParentNode.ParentNode.ParentNode.Name.Equals("ContainerSingleStatementBody"))
                    {
                        state.IncrementIndent();
                    }
                    break;
                case "CaseStatement":
                    ProcessSqlNodeList(contentElement.SelectNodes("ContainerOpen"), state);
                    state.IncrementIndent();
                    ProcessSqlNodeList(contentElement.SelectNodes("Input"), state);
                    ProcessSqlNodeList(contentElement.SelectNodes("When"), state);
                    ProcessSqlNodeList(contentElement.SelectNodes("CaseElse"), state);
                    if (Options.ExpandCaseStatements)
                    {
                        state.BreakExpected = true;
                    }
                    ProcessSqlNodeList(contentElement.SelectNodes("ContainerClose"), state);
                    state.DecrementIndent();
                    break;
                case "When":
                case "Then":
                case "CaseElse":
                    if (Options.ExpandCaseStatements)
                    {
                        state.BreakExpected = true;
                    }
                    ProcessSqlNodeList(contentElement.SelectNodes("ContainerOpen"), state);
                    ProcessSqlNodeList(contentElement.SelectNodes("ContainerContentBody"), state.IncrementIndent());
                    ProcessSqlNodeList(contentElement.SelectNodes("Then"), state);
                    state.DecrementIndent();
                    break;
                case "And":
                case "Or":
                    if (Options.ExpandBooleanExpressions)
                    {
                        state.BreakExpected = true;
                    }
                    ProcessSqlNodeList(contentElement.SelectNodes("*"), state);
                    break;
                case "MultiLineComment":
                    if (state.SpecialRegionActive == SpecialRegionType.NoFormat && contentElement.InnerXml.ToUpperInvariant().Contains("[/NOFORMAT]"))
                    {
                        XmlNode sqlTreeFragment = ExtractXmlBetween(state.RegionStartNode, contentElement);
                        TSqlIdentityFormatter tSqlIdentityFormatter = new TSqlIdentityFormatter(Options.HTMLColoring);
                        state.AddOutputContentRaw(tSqlIdentityFormatter.FormatSQLTree(sqlTreeFragment));
                        state.SpecialRegionActive = null;
                        state.RegionStartNode = null;
                    }
                    else if (state.SpecialRegionActive == SpecialRegionType.Minify && contentElement.InnerXml.ToUpperInvariant().Contains("[/MINIFY]"))
                    {
                        XmlNode fragmentNode = ExtractXmlBetween(state.RegionStartNode, contentElement);
                        TSqlObfuscatingFormatter tSqlObfuscatingFormatter = new TSqlObfuscatingFormatter();
                        if (HTMLFormatted)
                        {
                            state.AddOutputContentRaw(Utils.HtmlEncode(tSqlObfuscatingFormatter.FormatSQLTree(fragmentNode)));
                        }
                        else
                        {
                            state.AddOutputContentRaw(tSqlObfuscatingFormatter.FormatSQLTree(fragmentNode));
                        }
                        state.SpecialRegionActive = null;
                        state.RegionStartNode = null;
                    }
                    WhiteSpace_SeparateComment(contentElement, state);
                    state.AddOutputContent("/*" + contentElement.InnerText + "*/", "SQLComment");
                    if (contentElement.ParentNode.Name.Equals("SqlStatement") || (contentElement.NextSibling != null && contentElement.NextSibling.Name.Equals("WhiteSpace") && Regex.IsMatch(contentElement.NextSibling.InnerText, "(\\r|\\n)+")))
                    {
                        state.BreakExpected = true;
                    }
                    else
                    {
                        state.WordSeparatorExpected = true;
                    }
                    if (!state.SpecialRegionActive.HasValue && contentElement.InnerXml.ToUpperInvariant().Contains("[NOFORMAT]"))
                    {
                        state.AddOutputLineBreak();
                        state.SpecialRegionActive = SpecialRegionType.NoFormat;
                        state.RegionStartNode = contentElement;
                    }
                    else if (!state.SpecialRegionActive.HasValue && contentElement.InnerXml.ToUpperInvariant().Contains("[MINIFY]"))
                    {
                        state.AddOutputLineBreak();
                        state.SpecialRegionActive = SpecialRegionType.Minify;
                        state.RegionStartNode = contentElement;
                    }
                    break;
                case "SingleLineComment":
                case "SingleLineCommentCStyle":
                    if (state.SpecialRegionActive == SpecialRegionType.NoFormat && contentElement.InnerXml.ToUpperInvariant().Contains("[/NOFORMAT]"))
                    {
                        XmlNode sqlTreeFragment2 = ExtractXmlBetween(state.RegionStartNode, contentElement);
                        TSqlIdentityFormatter tSqlIdentityFormatter2 = new TSqlIdentityFormatter(Options.HTMLColoring);
                        state.AddOutputContentRaw(tSqlIdentityFormatter2.FormatSQLTree(sqlTreeFragment2));
                        state.SpecialRegionActive = null;
                        state.RegionStartNode = null;
                    }
                    else if (state.SpecialRegionActive == SpecialRegionType.Minify && contentElement.InnerXml.ToUpperInvariant().Contains("[/MINIFY]"))
                    {
                        XmlNode fragmentNode2 = ExtractXmlBetween(state.RegionStartNode, contentElement);
                        TSqlObfuscatingFormatter tSqlObfuscatingFormatter2 = new TSqlObfuscatingFormatter();
                        if (HTMLFormatted)
                        {
                            state.AddOutputContentRaw(Utils.HtmlEncode(tSqlObfuscatingFormatter2.FormatSQLTree(fragmentNode2)));
                        }
                        else
                        {
                            state.AddOutputContentRaw(tSqlObfuscatingFormatter2.FormatSQLTree(fragmentNode2));
                        }
                        state.SpecialRegionActive = null;
                        state.RegionStartNode = null;
                    }
                    WhiteSpace_SeparateComment(contentElement, state);
                    state.AddOutputContent(((contentElement.Name == "SingleLineComment") ? "--" : "//") + contentElement.InnerText.Replace("\r", "").Replace("\n", ""), "SQLComment");
                    state.BreakExpected = true;
                    state.SourceBreakPending = true;
                    if (!state.SpecialRegionActive.HasValue && contentElement.InnerXml.ToUpperInvariant().Contains("[NOFORMAT]"))
                    {
                        state.AddOutputLineBreak();
                        state.SpecialRegionActive = SpecialRegionType.NoFormat;
                        state.RegionStartNode = contentElement;
                    }
                    else if (!state.SpecialRegionActive.HasValue && contentElement.InnerXml.ToUpperInvariant().Contains("[MINIFY]"))
                    {
                        state.AddOutputLineBreak();
                        state.SpecialRegionActive = SpecialRegionType.Minify;
                        state.RegionStartNode = contentElement;
                    }
                    break;
                case "String":
                case "NationalString":
                    {
                        WhiteSpace_SeparateWords(state);
                        string text = null;
                        text = ((!contentElement.Name.Equals("NationalString")) ? ("'" + contentElement.InnerText.Replace("'", "''") + "'") : ("N'" + contentElement.InnerText.Replace("'", "''") + "'"));
                        state.AddOutputContent(text, "SQLString");
                        state.WordSeparatorExpected = true;
                        break;
                    }
                case "BracketQuotedName":
                    WhiteSpace_SeparateWords(state);
                    state.AddOutputContent("[" + contentElement.InnerText.Replace("]", "]]") + "]");
                    state.WordSeparatorExpected = true;
                    break;
                case "QuotedString":
                    WhiteSpace_SeparateWords(state);
                    state.AddOutputContent("\"" + contentElement.InnerText.Replace("\"", "\"\"") + "\"");
                    state.WordSeparatorExpected = true;
                    break;
                case "Comma":
                    if (Options.TrailingCommas)
                    {
                        WhiteSpace_BreakAsExpected(state);
                        state.AddOutputContent(FormatOperator(","), "SQLOperator");
                        if ((Options.ExpandCommaLists && !contentElement.ParentNode.Name.Equals("DDLDetailParens") && !contentElement.ParentNode.Name.Equals("FunctionParens") && !contentElement.ParentNode.Name.Equals("InParens")) || (Options.ExpandInLists && contentElement.ParentNode.Name.Equals("InParens")))
                        {
                            state.BreakExpected = true;
                        }
                        else
                        {
                            state.WordSeparatorExpected = true;
                        }
                    }
                    else if ((Options.ExpandCommaLists && !contentElement.ParentNode.Name.Equals("DDLDetailParens") && !contentElement.ParentNode.Name.Equals("FunctionParens") && !contentElement.ParentNode.Name.Equals("InParens")) || (Options.ExpandInLists && contentElement.ParentNode.Name.Equals("InParens")))
                    {
                        state.WhiteSpace_BreakToNextLine();
                        state.AddOutputContent(FormatOperator(","), "SQLOperator");
                        if (Options.SpaceAfterExpandedComma)
                        {
                            state.WordSeparatorExpected = true;
                        }
                    }
                    else
                    {
                        WhiteSpace_BreakAsExpected(state);
                        state.AddOutputContent(FormatOperator(","), "SQLOperator");
                        state.WordSeparatorExpected = true;
                    }
                    break;
                case "Period":
                case "Semicolon":
                case "ScopeResolutionOperator":
                    state.WordSeparatorExpected = false;
                    WhiteSpace_BreakAsExpected(state);
                    state.AddOutputContent(FormatOperator(contentElement.InnerText), "SQLOperator");
                    break;
                case "Asterisk":
                case "EqualsSign":
                case "AlphaOperator":
                case "OtherOperator":
                    WhiteSpace_SeparateWords(state);
                    state.AddOutputContent(FormatOperator(contentElement.InnerText), "SQLOperator");
                    state.WordSeparatorExpected = true;
                    break;
                case "CompoundKeyword":
                    WhiteSpace_SeparateWords(state);
                    state.SetRecentKeyword(contentElement.Attributes["simpleText"].Value);
                    state.AddOutputContent(FormatKeyword(contentElement.Attributes["simpleText"].Value), "SQLKeyword");
                    state.WordSeparatorExpected = true;
                    ProcessSqlNodeList(contentElement.SelectNodes("MultiLineComment | SingleLineComment | SingleLineCommentCStyle"), state.IncrementIndent());
                    state.DecrementIndent();
                    state.WordSeparatorExpected = true;
                    break;
                case "OtherKeyword":
                case "DataTypeKeyword":
                    WhiteSpace_SeparateWords(state);
                    state.SetRecentKeyword(contentElement.InnerText);
                    state.AddOutputContent(FormatKeyword(contentElement.InnerText), "SQLKeyword");
                    state.WordSeparatorExpected = true;
                    break;
                case "PseudoName":
                    WhiteSpace_SeparateWords(state);
                    state.AddOutputContent(FormatKeyword(contentElement.InnerText), "SQLKeyword");
                    state.WordSeparatorExpected = true;
                    break;
                case "FunctionKeyword":
                    WhiteSpace_SeparateWords(state);
                    state.SetRecentKeyword(contentElement.InnerText);
                    state.AddOutputContent(contentElement.InnerText, "SQLFunction");
                    state.WordSeparatorExpected = true;
                    break;
                case "Other":
                case "MonetaryValue":
                case "Label":
                    WhiteSpace_SeparateWords(state);
                    state.AddOutputContent(contentElement.InnerText);
                    state.WordSeparatorExpected = true;
                    break;
                case "NumberValue":
                    WhiteSpace_SeparateWords(state);
                    state.AddOutputContent(contentElement.InnerText.ToLowerInvariant());
                    state.WordSeparatorExpected = true;
                    break;
                case "BinaryValue":
                    WhiteSpace_SeparateWords(state);
                    state.AddOutputContent("0x");
                    state.AddOutputContent(contentElement.InnerText.Substring(2).ToUpperInvariant());
                    state.WordSeparatorExpected = true;
                    break;
                case "WhiteSpace":
                    if (Regex.IsMatch(contentElement.InnerText, "(\\r|\\n)+"))
                    {
                        state.SourceBreakPending = true;
                    }
                    break;
                default:
                    throw new Exception("Unrecognized element in SQL Xml!");
            }
            if (contentElement.GetAttribute("hasError") == "1")
            {
                state.CloseClass();
            }
            if (indentLevel != state.IndentLevel)
            {
                throw new Exception("Messed up the indenting!! Check code/stack or panic!");
            }
        }

        private XmlNode ExtractXmlBetween(XmlNode startingElement, XmlNode endingElement)
        {
            XmlNode xmlNode = startingElement;
            XmlNode xmlNode2 = null;
            XmlNode xmlNode3 = null;
            XmlNode xmlNode4 = null;
            while (xmlNode != null && !xmlNode.Equals(endingElement))
            {
                if (xmlNode2 != null)
                {
                    XmlNode xmlNode5 = xmlNode.OwnerDocument.CreateNode(xmlNode.NodeType, xmlNode.Name, xmlNode.NamespaceURI);
                    if (xmlNode.Value != null)
                    {
                        xmlNode5.Value = xmlNode.Value;
                    }
                    if (xmlNode.Attributes != null)
                    {
                        foreach (XmlAttribute attribute in xmlNode.Attributes)
                        {
                            XmlAttribute xmlAttribute2 = xmlNode.OwnerDocument.CreateAttribute(attribute.Prefix, attribute.LocalName, attribute.NamespaceURI);
                            xmlAttribute2.Value = attribute.Value;
                            xmlNode5.Attributes.Append(xmlAttribute2);
                        }
                    }
                    if (xmlNode4 == null)
                    {
                        xmlNode4 = xmlNode5;
                        xmlNode3 = xmlNode5;
                    }
                    else if (xmlNode.Equals(xmlNode2.ParentNode) && xmlNode4.ParentNode != null)
                    {
                        xmlNode4 = xmlNode4.ParentNode;
                    }
                    else if (xmlNode.Equals(xmlNode2.ParentNode) && xmlNode4.ParentNode == null)
                    {
                        xmlNode5.AppendChild(xmlNode4);
                        xmlNode4 = xmlNode5;
                        xmlNode3 = xmlNode5;
                    }
                    else if (xmlNode.Equals(xmlNode2.NextSibling) && xmlNode4.ParentNode != null)
                    {
                        xmlNode4.ParentNode.AppendChild(xmlNode5);
                        xmlNode4 = xmlNode5;
                    }
                    else if (xmlNode.Equals(xmlNode2.NextSibling) && xmlNode4.ParentNode == null)
                    {
                        XmlNode xmlNode6 = xmlNode.OwnerDocument.CreateNode(xmlNode.ParentNode.NodeType, xmlNode.ParentNode.Name, xmlNode.ParentNode.NamespaceURI);
                        xmlNode3 = xmlNode6;
                        xmlNode3.AppendChild(xmlNode4);
                        xmlNode3.AppendChild(xmlNode5);
                        xmlNode4 = xmlNode5;
                    }
                    else
                    {
                        xmlNode4.AppendChild(xmlNode5);
                        xmlNode4 = xmlNode5;
                    }
                }
                XmlNode xmlNode7 = null;
                xmlNode7 = ((xmlNode2 != null && xmlNode.HasChildNodes && !xmlNode.Equals(xmlNode2.ParentNode)) ? xmlNode.FirstChild : ((xmlNode.NextSibling == null) ? xmlNode.ParentNode : xmlNode.NextSibling));
                xmlNode2 = xmlNode;
                xmlNode = xmlNode7;
            }
            return xmlNode3;
        }

        private string FormatKeyword(string keyword)
        {
            if (!KeywordMapping.TryGetValue(keyword, out var value))
            {
                value = keyword;
            }
            if (Options.UppercaseKeywords)
            {
                return value.ToUpperInvariant();
            }
            return value.ToLowerInvariant();
        }

        private string FormatOperator(string operatorValue)
        {
            if (Options.UppercaseKeywords)
            {
                return operatorValue.ToUpperInvariant();
            }
            return operatorValue.ToLowerInvariant();
        }

        private void WhiteSpace_SeparateStatements(XmlElement contentElement, TSqlStandardFormattingState state)
        {
            if (!state.StatementBreakExpected)
            {
                return;
            }
            XmlElement xmlElement = FirstSemanticElementChild(contentElement);
            if (xmlElement == null || !xmlElement.Name.Equals("OtherKeyword") || state.GetRecentKeyword() == null || ((!xmlElement.InnerXml.ToUpperInvariant().Equals("SET") || !state.GetRecentKeyword().Equals("SET")) && (!xmlElement.InnerXml.ToUpperInvariant().Equals("DECLARE") || !state.GetRecentKeyword().Equals("DECLARE")) && (!xmlElement.InnerXml.ToUpperInvariant().Equals("PRINT") || !state.GetRecentKeyword().Equals("PRINT"))))
            {
                for (int num = Options.NewStatementLineBreaks; num > 0; num--)
                {
                    state.AddOutputLineBreak();
                }
            }
            else
            {
                for (int num2 = Options.NewClauseLineBreaks; num2 > 0; num2--)
                {
                    state.AddOutputLineBreak();
                }
            }
            state.Indent(state.IndentLevel);
            state.BreakExpected = false;
            state.AdditionalBreaksExpected = 0;
            state.SourceBreakPending = false;
            state.StatementBreakExpected = false;
            state.WordSeparatorExpected = false;
        }

        private XmlElement FirstSemanticElementChild(XmlElement contentElement)
        {
            XmlElement xmlElement = null;
            while (contentElement != null)
            {
                xmlElement = (XmlElement)contentElement.SelectSingleNode(string.Format("*[local-name() != '{0}' and local-name() != '{1}' and local-name() != '{2}' and local-name() != '{3}']", "WhiteSpace", "MultiLineComment", "SingleLineComment", "SingleLineCommentCStyle"));
                contentElement = ((xmlElement == null || (!xmlElement.Name.Equals("Clause") && !xmlElement.Name.Equals("DDLProceduralBlock") && !xmlElement.Name.Equals("DDLOtherBlock") && !xmlElement.Name.Equals("DDLDeclareBlock"))) ? null : xmlElement);
            }
            return xmlElement;
        }

        private void WhiteSpace_SeparateWords(TSqlStandardFormattingState state)
        {
            if (state.BreakExpected || state.AdditionalBreaksExpected > 0)
            {
                bool unIndentInitialBreak = state.UnIndentInitialBreak;
                if (unIndentInitialBreak)
                {
                    state.DecrementIndent();
                }
                WhiteSpace_BreakAsExpected(state);
                if (unIndentInitialBreak)
                {
                    state.IncrementIndent();
                }
            }
            else if (state.WordSeparatorExpected)
            {
                state.AddOutputSpace();
            }
            state.UnIndentInitialBreak = false;
            state.SourceBreakPending = false;
            state.WordSeparatorExpected = false;
        }

        private void WhiteSpace_SeparateComment(XmlElement contentElement, TSqlStandardFormattingState state)
        {
            if (state.CurrentLineHasContent && state.SourceBreakPending)
            {
                state.BreakExpected = true;
                WhiteSpace_BreakAsExpected(state);
            }
            else if (state.WordSeparatorExpected)
            {
                state.AddOutputSpace();
            }
            state.SourceBreakPending = false;
            state.WordSeparatorExpected = false;
        }

        private void WhiteSpace_BreakAsExpected(TSqlStandardFormattingState state)
        {
            if (state.BreakExpected)
            {
                state.WhiteSpace_BreakToNextLine();
            }
            while (state.AdditionalBreaksExpected > 0)
            {
                state.WhiteSpace_BreakToNextLine();
                state.AdditionalBreaksExpected--;
            }
        }
    }
}
